1,初识Java多线程
(1)线程简介
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。一个进程可以包含一个或多个线程。
(2)线程的四种状态
(3)自定义线程
自定义线程有两种方式:一种是继承Thread类,另一种是实现Runnable接口。
a,通过继承Thread类实现自定义线程步骤:定义类继承Thread;复写Thread类中的run方法;调用线程的start方法。
运行示例:
class Demo extends Thread{
int count = 0;
public void run(){
while(count<20){
count++;
System.out.println(currentThread().getName()+"\t"+count);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo d1 = new Demo();
Demo d2 = new Demo();
System.out.println("Thread1 start!");
d1.start();
System.out.println("Thread2 start!");
d2.start();
System.out.println("OK!!!");
}
}
b,通过实现Runnable接口实现自定义线程步骤:定义类实现Runnable接口;覆盖Runnable接口中的run方法;通过Thread类建立线程对象;将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
运行示例:
class Demo implements Runnable{
int count = 0;
public void run(){
while(count<20){
count++;
System.out.println(currentThread().getName()+"\t"+count);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
System.out.println("Thread1 start!");
d1.start();
System.out.println("Thread2 start!");
d2.start();
System.out.println("OK!!!");
}
}
2,线程同步
(1)同步的应用
多线程程序中,由于同时有多个线程并发执行,有时会带来严重的问题,甚至引发错误。因此,必须采用同步机制来防止类似情况的发生,即在一个线程完成操作之前,禁止其他对象访问该对象。
Java提供了同步方法和同步状态来实现同步,被宣布为同步的方法、对象或类数据,在任一时刻只能被一个线程使用。
同步方法指用synchronized修饰的方法。同步方法当一个线程执行时自动加锁,直到方法运行结束,锁解除。同步状态指用synchronized(Object)标识的语句块。语句块被一个线程执行时会自定加锁,直到运行结束,锁解除。
同步状态也就是同步代码块。
synchronized(对象){
需要被同步的代码;
}
同步的前提:a,必须要有两个或者两个以上的线程; b,必须是多个线程使用同一个锁。
运行示例:
public class SynchTest implements Runnable{
public static void main(String[] args) {
SynchTest st = new SynchTest();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
while((t1.isAlive())||(t2.isAlive()));
System.out.println("The test is end.");
}
public void run(){
String name = Thread.currentThread().getName();
show(name);
System.out.println(name+" is dead!");
}
private synchronized void show(String name){
for (int i = 1; i <= 500; i++) {
System.out.println("I am "+name+"--I have run "+i+" times.");
}
}
}
上述例程也可以改为用同步代码块来实现,只需把show方法改写成:
private void show(String name){
synchronized ("Test") {
for (int i = 1; i <= 500; i++) {
System.out.println("I am " + name + "--I have run " + i+ " times.");
}
}
}
(2)同步中使用的锁
同步代码块中使用的锁是synchronized(对象){ }中的对象。
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步方法使用的锁是this。
静态的同步方法使用的锁是该方法所在类的字节码文件对象。 类名.class
(3)死锁
死锁产生的原因:同步中嵌套同步。
产生死锁示例:
class Test implements Runnable{//产生死锁示例
private boolean flag;
Test(boolean flag){
this.flag = flag;
}
public void run(){
if(flag){
synchronized(MyLock.locka){
System.out.println("if locka");
synchronized(MyLock.lockb){
System.out.println("if lockb");
}
}
}
else{
synchronized(MyLock.lockb){
System.out.println("else lockb");
synchronized(MyLock.locka){
System.out.println("else locka");
}
}
}
}
}
class MyLock{
public static Object locka = new Object();
public static Object lockb = new Object();
}
public class BankDemo {
public static void main(String[] args) {
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
3,线程中基本方法
(1)start
public void start():使该线程开始执行;Java 虚拟机调用该线程的run方法。结果是两个线程并发地运行;当前线程(从调用返回给start方法)和另一个线程(执行其run方法)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
(2)run
public void run():如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法;否则,该方法不执行任何操作并返回。Thread 的子类应该重写该方法。
(3)sleep
public static void sleep(long millis)throwsInterruptedException:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
public static void sleep(long millis,int nanos)throwsInterruptedException:在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
(4)currentThread
public static Thread currentThread():返回对当前正在执行的线程对象的引用。
(5)yield
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
(6)getName
public final String getName():返回该线程的名称。
(7)interrupt
public void interrupt():结束线程的冻结状态,使线程回到运行状态中来。如果线程在调用 Object
类的 wait()
、wait(long)
或wait(long, int)
方法,或者该类的join()
、join(long)
、join(long, int)
、sleep(long)
或sleep(long, int)
方法过程中受阻,则其中断状态将被清除,它还将收到一个InterruptedException
。
(8)join
public final void join() throws InterruptedException:等待该线程终止。
(9)setDaemon
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
注意:了解更多用法,请下载JDK API 1.6.0中文版。
4,从类Object继承的方法
notify,notifyAll,wait:都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
5,Lock替换Synchronized
JDK1.5中提供了多线程升级解放方案。将同步Synchronized替换成Lock操作。将Object中的wait,notify,notifyAll替换成Condition对象中await,signal,signalAll。
运行示例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Resource1{//仓库
private String name;
private int count = 1;
private boolean flag = false;
final Lock lock = new ReentrantLock();
final Condition condition_pro = lock.newCondition();
final Condition condition_con = lock.newCondition();
public void set(String name) throws InterruptedException {
lock.lock();
try {
while (flag)
condition_pro.await();
this.name = name + "---" + count++;
System.out.println(Thread.currentThread().getName() + "...生产者..." + this.name);
flag = true;
condition_con.signal();
} finally {
lock.unlock();//释放锁的动作一定要执行
}
}
public void out() throws InterruptedException {
lock.lock();
try {
while (!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName() + "...消费者..." + this.name);
flag = false;
condition_pro.signal();
} finally {
lock.unlock();
}
}
}
class Producer1 implements Runnable{//生产者
private Resource1 res;
Producer1(Resource1 res){
this.res = res;
}
public void run(){
while(true){
try {
res.set("+商品+");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Consumer1 implements Runnable{//消费者
private Resource1 res;
Consumer1(Resource1 res){
this.res = res;
}
public void run(){
while(true){
try {
res.out();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class ProducerConsumerDemo1 {
public static void main(String[] args) {
Resource1 r = new Resource1();
Producer1 pro = new Producer1(r);
Consumer1 con = new Consumer1(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}