java学习(15)
1.多线程这篇开始写关于多线程的内容。
1.1线程是依赖于进程而存在的。
A:进程 正在运行的应用程序
B:线程 进程的执行路径,执行单元
1.2多线程的两种方案
方法一:继承Thread类,通过调用Thread类的start()方法,启动一个线程,
代码实现:
public class MyThreadTest extends Thread{ @Override public void run() { System.out.println("这是子线程的run方法"); } }public static void main(String[] args) { //创建MyThreadTest对象 MyThreadTest mt = new MyThreadTest(); //启动线程 mt.start(); }
方法二:实现Runable接口,再通过Thread类的构造方法,把实现Runable接口的类对象传进去,再调用start()方法,启动线程,代码实现:
public class MyThreadTest implements Runnable{ @Override public void run() { System.out.println("这是子线程的run方法"); } }public static void main(String[] args) { //创建MyThreadTest对象 MyThreadTest mt = new MyThreadTest(); //构造Thread对象 Thread t = new Thread(mt); //启动线程 t.start(); }
1.3多线程的有关问题:
A:启动线程用的方法:start()
B:start()和run()的区别
start():
1.开启线程 2.执行run()方法里面的代码
run():执行的是线程里面执行的代码,并不会开启线程,简单来说,就是创建对象,调方法
C:重写run():因为需求不同
D:线程不可以多次启动
1.4线程的调度和控制
A.线程休眠:(Thread.sleep(毫秒值))--可以设置线程的休眠时间
Thread.sleep(500);//休眠500毫秒
B.线程名称:(setName(),getName();)--可以获取和设置线程的名称。对于实现了Runnable接口的类,调用Thread.currentThread().getName()来获取当前线程的名称。
//设置线程名称 mt1.setName("ss1"); mt2.setName("ss2"); mt3.setName("ss3");
C.线程的调度及优先级(抢占cpu执行权抢占到的概率):setPriority(10)(注意默认值是5,区间在1-10之间)--可以设置线程的优先级
注意:设置了优先级,只能保证优先级高的线程抢占到CPU的概率大,但不是一定能抢到!!
//线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间 mt1.setPriority(10);
1.5经典卖票案例
1.5.1继承Thread卖票
public class MyThread extends Thread{ int ticket = 20; @Override public void run() { while(true){ //判断票数 if(ticket>0){ System.out.println(this.getName()+"正在卖"+ticket--+"张票"); } } } }public static void main(String[] args) { //创建mythread MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); MyThread mt3 = new MyThread(); //线程名称(setName(),getName();) mt1.setName("窗口一"); mt2.setName("窗口二"); mt3.setName("窗口三"); //启动线m mt1.start(); mt2.start(); mt3.start(); }
1.5.2实现Runnable卖票
public class MyThread implements Runnable{ int ticket = 20; @Override public void run() { while(true){ //判断票数 if(ticket>0){ System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票"); } } } }public static void main(String[] args) { //创建mythread MyThread mt = new MyThread(); //创建Thread对象 Thread t1 = new Thread(mt); Thread t2 = new Thread(mt); Thread t3 = new Thread(mt); //线程名称(setName(),getName();) t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); //启动线m t1.start(); t2.start(); t3.start(); }
分析:按照真实的情景加入了延迟后,发现出现了这样的两个问题:
A:相同的票卖了多次
B:出现了负数的票
原因:(1)CPU的一次操作必须是原子性的(操作是CPU执行一次就可以直接完成的)
(2)线程抢占随机性和延迟导致的
1.6多线程安全问题分析:
A:是否是多线程环境B:是否有共享数据
C:是否有多条语句操作共享数据
1.7解决多线程安全问题:线程安全执行效率就低,这里对于各种方法,对上述代码进行部分改写。
(1)同步代码块(测试不是同一个锁的情况,测试是同一个锁的情况)
synchronized(对象) {
需要被同步的代码。
}
分析:这里的对象是一个任意对象 ,相当于是一把锁,只要线程进去就把锁锁上:
需要同步的代码就是被线程执行的代码。对象是同一个的时候,线程安全:对象不是同一个的时候,线程不安全。上述代码块可以这么改写,这样就不会出现线程安全问题:
public class MyThread implements Runnable{ int ticket = 100; //创建锁对象 Object obj = new Object(); @Override public void run() { while(true){ synchronized(obj){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //判断票数 if(ticket>0){ System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票"); } } } } }
(2)锁对象问题
a:同步代码块(定义一个抽象类,里面专门定义一个锁)
public abstract class MyLock { //定义锁 public static final Object obj = new Object(); }
b:同步方法(仅适用于实现runable接口)锁是:this
public synchronized void sellTicket(){
同步代码
}
//同步方法:同步方法是将synchronized关键字加到方法上,//同步方法的锁是this private synchronized void sellTicket() { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //判断票数 if(ticket>0){ System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票"); } }
c:静态同步方法 锁是:类的字节码对象
public static synchronized void sellTicket() {
需要同步的代码
}
//静态同步方法,他的锁是本类的字节码文件对象:类名.class public static synchronized void sellTicket(){ try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //判断票数 if(ticket>0){ System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票"); } }
1.8匿名内部类的方式使用多线程
new Thread() {
public void run() {
...
}
}.start();
new Thread(new Runnable(){
public void run() {
...
}
}).start();
示例:
//方法一 new Thread(){ @Override public void run() { System.out.println("方法一的线程"); } }.start(); //方法二 new Thread(new Runnable() { @Override public void run() { System.out.println("方法二的线程"); } }).start();
1.9 JDK5的Lock锁:我们之前造的所有的锁都没有手动释放锁
static Lock lock = new ReentrantLock();
加锁:lock.lock();
释放锁:lock.unlock();
1.10死锁问题:同步嵌套,锁里面套了一个锁,出现同步嵌套对上面代码改写:
public class MyThread implements Runnable{ int ticket = 100; //static Lock lock = new ReentrantLock(); static Lock lock = new ReentrantLock(); @Override public void run() { while(true){ try { //加锁:lock.lock(); lock.lock(); //判断票数 if(ticket>0){ System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票"); } }finally{ //释放锁:lock.unlock(); lock.unlock(); } } } }
示例:
public class MyLock { //定义两个锁 public static final Object objA = new Object(); public static final Object objB = new Object(); }
1.11线程等待和唤醒机制public class Deadlock extends Thread{ boolean flag; //有参的构造方法 public Deadlock(boolean flag){ this.flag = flag; }; //run方法 @Override public void run() { if(flag){ synchronized(MyLock.objA){ System.out.println("if"+"MyLock.objA锁"); synchronized(MyLock.objB){ System.out.println("if"+"MyLock.objB锁"); } } }else{ synchronized(MyLock.objB){ System.out.println("else"+"MyLock.objB锁"); synchronized(MyLock.objA){ System.out.println("else"+"MyLock.objA锁"); } } } } }
锁对象调用wait()
唤醒对象调用notify()
示例:
运行:public abstract class MyLock { //定义锁 public static final Object obj = new Object(); } public class NotifyThread extends Thread{ @Override public void run() { //唤醒等待线程 synchronized(MyLock.obj){ MyLock.obj.notify(); } } } public class WaitThread extends Thread{ @Override public void run() { synchronized(MyLock.obj){ try { MyLock.obj.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(this.getName()+"唤醒了"); } } public static void main(String[] args) { //创建线程对象 WaitThread wt = new WaitThread(); //设置名称 wt.setName("awakeThread"); //让进程等待 wt.start(); //2秒后唤醒 try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //唤醒对象调用notify() //创建唤醒线程对象 NotifyThread nt = new NotifyThread(); nt.start(); }
注意:wait()线程等待,在等待的同时释放锁,而sleep()方法在执行的过程中是不会释放锁的
1033

被折叠的 条评论
为什么被折叠?



