java学习(15)

来源:互联网 发布:网络打印机ip地址查询 编辑:程序博客网 时间:2024/06/18 09:44

java学习(15)

这篇开始写关于多线程的内容。

1.多线程
1.1线程是依赖于进程而存在的。
A:进程正在运行的应用程序
B:线程进程的执行路径,执行单元

1.2多线程的两种方案

方法一:继承Thread类,通过调用Thread类的start()方法,启动一个线程,

代码实现:

public class MyThreadTest extends Thread{@Overridepublic 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{@Overridepublic 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;@Overridepublic void run() {while(true){//判断票数if(ticket>0){System.out.println(this.getName()+"正在卖"+ticket--+"张票");}}}}
public static void main(String[] args) {//创建mythreadMyThread mt1 = new MyThread();MyThread mt2 = new MyThread();MyThread mt3 = new MyThread();//线程名称(setName(),getName();)mt1.setName("窗口一");mt2.setName("窗口二");mt3.setName("窗口三");//启动线mmt1.start();mt2.start();mt3.start();}


1.5.2实现Runnable卖票

public class MyThread implements Runnable{int ticket = 20;@Overridepublic void run() {while(true){//判断票数if(ticket>0){System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");}}}}
public static void main(String[] args) {//创建mythreadMyThread 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("窗口三");//启动线mt1.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();@Overridepublic void run() {while(true){synchronized(obj){try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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关键字加到方法上,
//同步方法的锁是thisprivate synchronized void sellTicket() {try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}//判断票数if(ticket>0){System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");}}

c:静态同步方法锁是:类的字节码对象
public static synchronized void sellTicket() {
需要同步的代码

}

//静态同步方法,他的锁是本类的字节码文件对象:类名.classpublic static synchronized void sellTicket(){try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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(){@Overridepublic void run() {System.out.println("方法一的线程");}}.start();//方法二new Thread(new Runnable() {@Overridepublic void run() {System.out.println("方法二的线程");}}).start();


1.9 JDK5的Lock锁:我们之前造的所有的锁都没有手动释放锁
static Lock lock = new ReentrantLock();
加锁:lock.lock();

释放锁:lock.unlock();


对上面代码改写:

public class MyThread implements Runnable{int ticket = 100;//static Lock lock = new ReentrantLock();static Lock lock = new ReentrantLock();@Overridepublic void run() {while(true){try {//加锁:lock.lock();lock.lock();//判断票数if(ticket>0){System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");}}finally{//释放锁:lock.unlock();lock.unlock();}}}}

1.10死锁问题:同步嵌套,锁里面套了一个锁,出现同步嵌套

示例:

public class MyLock {//定义两个锁public static final Object objA = new Object();public static final Object objB = new Object();}

public class Deadlock extends Thread{boolean flag;//有参的构造方法public Deadlock(boolean flag){this.flag = flag;};//run方法@Overridepublic 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锁");}}}}}

1.11线程等待和唤醒机制

锁对象调用wait()

唤醒对象调用notify()

示例:

public abstract class MyLock {//定义锁public static final Object obj = new Object();}public class NotifyThread extends Thread{@Overridepublic void run() {//唤醒等待线程synchronized(MyLock.obj){MyLock.obj.notify();}}}public class WaitThread extends Thread{@Overridepublic void run() {synchronized(MyLock.obj){try {MyLock.obj.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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 blocke.printStackTrace();}//唤醒对象调用notify()//创建唤醒线程对象NotifyThread nt = new NotifyThread();nt.start();}
运行:


注意:wait()线程等待,在等待的同时释放锁,而sleep()方法在执行的过程中是不会释放锁的
0 0