java基础复习之多线程Thread 十三

来源:互联网 发布:mysql 5.7.20 编辑:程序博客网 时间:2024/06/05 01:14
多线程:就是应用程序多条执行路径
进程:正在运行的程序,

  线程: 进程的执行单元,一条执行路径



线程的简单使用方式一:

package thread;/** *  * @author Angus *getName() 返回该线程的名称。 */public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 30; i++) {System.out.println(getName()+"hello");}}}

ThreadDemo测试;

package thread;/** * @author Angus * *多线程:就是应用程序多条执行路径 *进程:正在运行的程序, *线程: 进程的执行单元,一条执行路径 * *实现? *线程类Thread: *A;定义一个类基础Thread类,重写run方法,start()方法启动线程。 *B:实现Runnable接口,重写run方法 * */public class ThreadDemo{public static void main(String[] args) {MyThread mt = new MyThread();MyThread mt2 = new MyThread();mt.start();//mt.start(); //Exception in thread "main" java.lang.IllegalThreadStateException////连续开始两次线程报错。。分析异常开启两个线程需要两个线程对象mt.setName("周杰伦");mt2.setName("周润发");mt2.start();}}

线程的简单使用方式二;

package thread;/** *  * @author Angus *B:实现Runnable接口,重写run方法 */public class MyRunnableDemo {public static void main(String[] args) {MyRunnable my = new MyRunnable();//数显runnable的发类没有start的方法,启动必须启动start方法//所以需要考虑Thread类Thread t = new Thread(my);Thread t2 = new Thread(my);t.setName("习大大");t2.setName("王岐山");t.start();t2.start();}}

MyRunnable类:

package thread;/** * @author Angus 实现Runnable接口,重写run方法 public static Thread currentThread() *         返回对当前正在执行的线程对象的引用。 */public class MyRunnable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 30; i++) {System.out.println(Thread.currentThread().getName() + "  hello");}}}
运行结果差不多。。。。

会发现第一种每次需要new两个对象,创建两个线程,走了两次run,第二种只创建了一个资源对象,这样数据和操作分离更好一些,一般开发中使用第二种多一些。。。


线程的生命周期:



多线程应用的场景:

运行都会卡死,建议DOS测试,这样可以关闭,eclipse只能关闭软件了。。。

方式一:

package thread2;/** *  * @author Angus *多线程应用的场景 *窗口卖票 * *有一趟火车,票不多了,还有200张,四个窗口卖票 * *使用多线程模拟卖票 *方式1 继承Thread类 *方式2实现Runnable接口 * */public class ThreadTest {public static void main(String[] args) {TicketTread tt1 = new TicketTread();TicketTread tt2 = new TicketTread();TicketTread tt3 = new TicketTread();TicketTread tt4 = new TicketTread();tt1.setName("窗口1");tt2.setName("窗口2");tt3.setName("窗口3");tt4.setName("窗口4");tt1.start();tt2.start();tt3.start();tt4.start();}}

TicketTread
package thread2;/** * @author Angus *方式一 */public class TicketTread extends Thread {@Overridepublic void run() {int tickets = 200;while(true){if(tickets>0){System.out.println(getName()+" 正在出售"+(tickets--)+"张票");}}}}

这样运行会CPU报表。。。直接死机,

原因: 每个线程new的时候都是一个新的200,都走run方法,,死循环。。

改进;

TicketTread

package thread2;/** * @author Angus *方式一 */public class TicketTread extends Thread {private static int tickets = 200;@Overridepublic void run() {while(true){if(tickets>0){System.out.println(getName()+" 正在出售"+(tickets--)+"张票");}}}}

而static修饰的成员变量比较占用资源,去掉更换第二种方式测试

package thread2;/** *  * @author Angus *方式二 */public class TicketRunnanble implements Runnable {private int tickets = 200;@Overridepublic void run() {while(true){if(tickets>0){System.out.println(Thread.currentThread().getName()+" 正在出售"+(tickets--)+"张票");}}}}

Runnable测试:

package thread2;/** *  * @author Angus *方式二 */public class RunnableTest {public static void main(String[] args) {TicketRunnanble tr = new TicketRunnanble();Thread t1 = new Thread(tr); Thread t2 = new Thread(tr); Thread t3 = new Thread(tr); Thread t4 = new Thread(tr); t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t4.setName("窗口4");t1.start();t2.start();t3.start();t4.start();}}

运行发现也会出现卡死的现象,和之前一样出现一样的票卖了多次的情况。

加一个睡眠操作:

package thread2;/** *  * @author Angus *方式二 */public class TicketRunnanble implements Runnable {private int tickets = 200;@Overridepublic void run() {while(true){if(tickets>0){try {Thread.sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+" 正在出售"+(tickets--)+"张票");}}}}


会发现出现了负数:



甚至还有-2

当剩余最后一张票的时候,线程判断 >0 ,四个线程都进来了,这样就会出现负数的情况在 t--的情况下。而且在ticket--的情况下 也会出现相同票数的情况。。


解决:引入线程锁  synchronized

package thread2;/** *  * @author Angus  * 解决出现负数和相同数的方法;  * 在出问题的代码中加一个锁 找问题代码:  * 1 找共享数据  * 2 共享数据中有没有多条语句  * 3是不是在多线程中  * 怎么锁?  * synchronized(锁对象) * 注意;多个线程必须使用同一把锁 */public class TicketRunnanble implements Runnable {private int tickets = 100;private Object obj = new Object();@Overridepublic void run() {while (true) {synchronized (obj) {if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ " 正在出售" + (tickets--) + "张票");}}}}}

这样运行就没有问题了,不会出现负数和相同的数据了。


方法锁的引入:

package thread2;/** *  * @author Angus synchronized(锁对象) *  *         锁对象除了object还可以试其它对象? 结论; 同步代码锁可以试任意对象 */class Demo {// 测试发现没问题。}public class TicketRunnanble implements Runnable {private int tickets = 100;private Object obj = new Object();private Demo d = new Demo();@Overridepublic void run() {int x = 0;while (true) {if (x % 2 == 0) {synchronized (d) {if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ " 正在出售" + (tickets--) + "张票");}}} else {synchronized (d) {if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ " 正在出售" + (tickets--) + "张票");}}}x++;}}}

测试没有问题,同一个锁对象。。。把else抽取成方法测试;

package thread2;/** *  * @author Angus synchronized(锁对象) *  *         锁对象除了object还可以试其它对象? 结论; 同步代码锁可以试任意对象 */class Demo {// 测试发现没问题。}public class TicketRunnanble implements Runnable {private int tickets = 100;private Object obj = new Object();private Demo d = new Demo();@Overridepublic void run() {int x = 0;while (true) {if (x % 2 == 0) {synchronized (d) {if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ " 正在出售" + (tickets--) + "张票");}}} else {check();}x++;}}private void check() {synchronized (d) {if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ " 正在出售" + (tickets--) + "张票");}}}}

这样运行也没有问题,OK,线程抽取方法锁测试;

private synchronized void check() {if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ " 正在出售" + (tickets--) + "张票");}}
方法中加了锁,代码中就不需要加了,测试。。:



出现了一样的,因为方法锁也没有确认同一个对象,if中用的是demo对象。。

问题一:测试返现同步方法锁的对象为this对象修改代码:

synchronized (this)

这样就和同步方法为同一个对象,线程就没有问题了。。

问题二:静态方法锁的对象是?

测试返现为:

是当前类的字节码文件对象
类名.class

修改代码:

synchronized (TicketRunnanble.class) 


线程的死锁:


MyLock

package thread2;public class MyLock {public static final Object obja = new Object();public static final Object objb = new Object();}

DieLock

package thread2;/** * @author Angus *  *死锁的问题: *五个人吃饭,菜齐了,一人只有一只筷子,每个人思考,把筷子借给别人,然后别人吃完自己再吃, *假如五个人都饿了,都拿着自己的筷子,并且等着别人放下筷子,然后使用,但是没有一个人愿意先放下筷子 *所以就出现了死锁。。 * */public class DieLock extends Thread{private boolean flag;public DieLock(boolean flag) {this.flag = flag;}@Overridepublic void run() {if(flag){synchronized (MyLock.obja) {System.out.println("true---obja");//当线程d1走到这里线程2抢到了起源  线程d1需要 MyLock.objbsynchronized (MyLock.objb) {System.out.println("true---objb");}}}else{synchronized (MyLock.objb) {System.out.println("true---objb");//当线程d2走到这里的时候,线程d2需要 MyLock.objasynchronized (MyLock.obja) {System.out.println("true---obja");}}}//谁也不放锁,就会造成死锁。。。。。}}

测试DieLockDemo

package thread2;public class DieLockDemo {public static void main(String[] args) {DieLock d1 = new DieLock(true);DieLock d2 = new DieLock(false);d1.start();d2.start();}}

结果;  出现了死锁。。。。




线程间的通讯:

线程间的通讯:不同种类的线程对共享数据的操作功能。。。


 * 举例:以学生做为资源
 * 学生类
 * 设置学生类
 * 获取学生类
 * 测试类


Student

package thread3;public class Student {String name;int age;}
SetStudent
package thread3;public class SetStudent implements Runnable {private Student s;public SetStudent(Student s) {this.s = s;}@Overridepublic void run() {int x = 0;while(true){if(x %2 ==0){s.name = "呵呵1";s.age = 28;}else{s.name = "哈哈2";s.age = 11;}}}}
GetStudent
package thread3;public class GetStudent implements Runnable{private Student s;public GetStudent(Student s) {this.s = s;}@Overridepublic void run() {while(true){System.out.println(s.name+s.age);}}}

StudentTest
package thread3;/** * @author Angus *线程间的通讯: *举例:以学生做为资源 *学生类 *设置学生类 *获取学生类 *测试类 * *测试发现,出现年龄和姓名的交叉问题。。 */public class StudentTest {public static void main(String[] args) {Student s = new Student();SetStudent ss = new SetStudent(s);GetStudent gs = new GetStudent(s);Thread t1 = new Thread(ss);Thread t2 = new Thread(gs);t1.start();t2.start();}}

结果:


测试发现,出现年龄和姓名的交叉问题。。在run方法下,线程的随机性,出现了这样的情况。。

需要同步锁解决。

@Overridepublic void run() {//t1进来int x = 0;while(true){synchronized (s) {if(x %2 ==0){s.name = "呵呵 ";s.age = 28;}else{s.name = "哈哈 ";s.age = 11;}}x++; //x=1}}



@Overridepublic void run() {while(true){synchronized (s) {System.out.println(s.name+s.age);}}}

运行解决。。。

但是发现在控制台一打一大片一个线程的数据,能不能每个线程交叉进程呢?

线程的等待:

*发现输出的内容是一大片一大片输出的,能不能一个输出后再输出另一个交叉进行呢? * *针对输出: *判断是否有数据,有就输出,没有待设置数据 *针对设置: *判断是否有数据,有就等待输出,否则输出 *等待唤醒机制; *wait();  //等待 *notify();//唤醒

修改代码:

Student;

package thread3;public class Student {String name;int age;boolean flag = false; //ture 有数据}

SetStudent

@Overridepublic void run() {//t1进来int x = 0;while(true){synchronized (s) {//如果有数据等待if(s.flag){try {s.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if(x %2 ==0){s.name = "呵呵 ";s.age = 28;}else{s.name = "哈哈 ";s.age = 11;}//修改标记s.flag = true;s.notify();}x++; //x=1}}
GetStudent
@Overridepublic void run() {while(true){synchronized (s) {if(!s.flag){try {s.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}System.out.println(s.name+s.age);s.flag = false;s.notify();}}}

运行结果;全部交叉进行。。。。




线程的优先级;

PriorityDemo

package thread4;public class PriorityDemo implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"  ----"+i);}}}

PriorityTset
package thread4;/** * 线程的优先级 * @author Angus * *getPriority() 返回线程的优先级。 *setPriority(int newPriority)  更改线程的优先级。 * *运行发现,优先级并不是每次都最先运行 *注意:优先级只是在一定的程度上,让线程优先级的多执行次数 */public class PriorityTset {public static void main(String[] args) {PriorityDemo pd = new PriorityDemo();Thread t1 = new Thread(pd);Thread t2 = new Thread(pd);Thread t3 = new Thread(pd);t1.setName("哈哈1");t2.setName("哈哈2");t3.setName("哈哈3");t1.setPriority(1); //默认5范围1-10t2.setPriority(5); t3.setPriority(10); System.out.println(t1.getPriority());System.out.println(t2.getPriority());System.out.println(t3.getPriority());t1.start();t2.start();t3.start();}}

其它API中的方法:

public static void yield()暂停当前正在执行的线程对象,并执行其他线程。 

让线程更和谐一些,一般不怎么用。。。。wait线程唤醒可以替换。。

public final void join()
                throws InterruptedException等待该线程终止。 

也是加入线程,可以让一个线程加入到运行中的线程中。。。。。前提是线程启动start了。。

public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 
该方法必须在启动线程前调用。 


该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。 




最后附上JDK使用文档API 下载








最后附上JDK使用文档API 下载
1 0
原创粉丝点击