多线程
线程的创建和使用
方式一:继承于Thread类
start()的作用:①启动当前线程②调用当前线程的run()
注意:不能直接通过调用run()的方法启动线程,可以再创建一个线程的对象,不可以让已经start()的线程去执行。
class MyThread extends Thread{public void run(){//重写的run方法}}public class thread_demo {public static void main(String[] args) {MyThread t1=new MyThread();t1.start();}}
方式二:创建Thread类的匿名子类的方式
public class thread_demo {public static void main(String[] args) {new Thread(){public void run(){//重写的run方法}}.start();}}
方式三:实现Runnable接口
class MyThread implements Runnable{public void run(){//重写的run方法}}public class thread_demo {public static void main(String[] args) {MyThread mythread=new MyThread();Thread t1=new Thread(mythread);t1.start();}}
方式四:JDK5.0新增 实现Callable接口
如何理解Callable比Runnable接口创建多线程方式强大
1.call()可以有返回值
2.call()可以抛出异常,被外面的操作捕获,获取异常的信息
3.call()支持泛型
package callable;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;//1.创建一个Callable的实现类class num_thread implements Callable{@Override//2.实现call方法,将此线程需要执行的操作声明在call()中public Object call() throws Exception {int sum=0;for (int i = 0; i < 20; i++) {System.out.println(i);sum+=i;}return sum;}}public class callable_interface {public static void main(String[] args) {//3.创建Callable接口实现类的对象num_thread n=new num_thread();//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象FutureTask futureTask = new FutureTask(n);//5.将FutureTask的对象作为参数,传递到Thread类的构造器中,创建Thread对象,并调用start()方法new Thread(futureTask).start();try {//6.获取Callable中call()方法的返回值//get()返回值即为FutureTask构造器参数Callable实现类重写的call()返回值Object sum = futureTask.get();//get方法返回值即为FutureTask构造器参数Callable实现类重写的call()方法的返回值System.out.println(sum);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}
方式五:使用线程池
Thread的常用方法
start():启动当前线程,调用当前线程的run()。run():通常需要重写Thread类中的此方法,将创建的线程需要执行的操作声明在此方法中。currentThread():静态方法,返回执行当前代码的线程。getName():获取当前线程的名字。setName():设置当前线程的名字。yield():释放当前CPU的执行权。join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,知道线程b完全执行完以后,线程a才结束阻塞状态。stop():已过时,当执行次方法时,强制结束当前线程。sleep(long millitime):让当前线程睡眠指定的毫秒。
同步
同步机制,解决线程安全问题
说明:
1.操作共享数据的代码,即为需要被同步的代码.(不能包含代码多了,也不能包含代码少了)
2.有共享数据才有线程安全问题,共享数据:多个线程共同操作的变量。
3.同步监视器,俗称:锁。任何一个类的对象都可以充当锁。
要求:多个线程必须共用同一把锁。
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
解决线程安全的方法
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
eg1:[用构造的对象当锁] main函数造了三个 window1 对象,构造obj对象时需要用static;class window1 extends Thread{private static int ticket=100;private static Object obj=new Object();public void run(){whlie(true){synchronized(obj){if(ticket>0){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(getName()+":"+ticket);ticket--;}else{break;}}}}}public class demo1 {public static void main(String[] args) {window1 t1=new window1();window1 t2=new window1();window1 t3=new window1();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
eg2:[this当锁] main函数只构造了一个 window2 对象,锁可以用this,此时的this是唯一的window2对象;class window2 extends Thread{private static int ticket=100;public void run(){whlie(true){synchronized(this){if(ticket>0){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(getName()+":"+ticket);ticket--;}else{break;}}}}}public class demo2 {public static void main(String[] args) {window2 w=new window2();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
eg3:[类当锁] 类也是对象(反射)Class clazz=window3.classclass window3 extends Thread{private static int ticket=100;private static Object obj=new Object();public void run(){whlie(true){synchronized(window3.class){if(ticket>0){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(getName()+":"+ticket);ticket--;}else{break;}}}}}public class demo3 {public static void main(String[] args) {window3 w=new window3();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
方式二:同步方法
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
eg4:class window4 extends Thread{private static int ticket=100;public void run(){while(true){show();}}}private synchronized void show(){if(ticket>0){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(getName()+":"+ticket);ticket--;}}public class demo3 {public static void main(String[] args) {window3 w=new window3();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
eg5:class window5 implemens Runnable{private int ticket=100;public void run(){while(true){show();}}}private static synchronized void show(){if(ticket>0){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();} System.out.println(Thread.currentThread().getName()+":"+ticket);ticket--;}}public class demo3 {public static void main(String[] args) {window5 t1=new window5();window5 t2=new window5();window5 t3=new window5();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
方式三:Lock锁
JDK 5.0新增class window extends Thread{private static int ticket=100;private ReentrantLock lock=new ReentrantLock();//1.实例化ReentrantLockpublic void run(){whlie(true){try{lock.lock();//2.调用lock()方法if(ticket>0){try{Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}System.out.println(getName()+":"+ticket);ticket--;}else{break;}}finally{lock.unlock();//3.调用解锁方法unlock()}}}}public class demo3 {public static void main(String[] args) {window3 w=new window3();Thread t1=new Thread(w);Thread t2=new Thread(w);Thread t3=new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}
synchronized与Lock的异同
1.相同:都可以解决线程安全问题
2.不同:synchronized机制在执行完相应的同步代码块以后,自动释放同步监视器
Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unlock());
优先使用顺序:Lock->同步代码块(已经进入了方法体,分配了相应资源)->同步方法(在方法体之外)
例题:两个储户分别向同一个账户存3000元,每次存1000,存三次
package test;class account{private double balance;public account(double balance){this.balance=balance;}public synchronized void deposit(double m) {if (m > 0) {balance += m;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "存钱成功,余额为:" + balance);}}}class customer extends Thread{private account a;public customer(account a){this.a=a;}@Overridepublic void run() {for (int i = 0; i < 3; i++) {a.deposit(1000);}}}public class account_test {public static void main(String[] args) {account a=new account(0);customer c1=new customer(a);customer c2=new customer(a);c1.setName("甲");c2.setName("乙");c1.start();c2.start();}}
