1多线程:
简单概念:Ctrl+Alt+Del(也就是在0数字旁边的.)看到进程了嘛!进程里面执行的程序里面就有很多的线程,当然你是看不到的懂吗?这都是编写代码的时候,程序员写的。当然你看过你打开软件的时候跳出了另外一个软件(也就是广告)吗?这就是又创建了一个进程,而这个进行里面有很多的线程,不是同时执行的哦,只是CPU在做着快速的切换。
一个程序是一个进程,而一个进程里面可以有多个线程执行不同的任务。
(线程运行状态):其中有一个(临时状态)阻塞,具备执行资格,但没有执行权,可能cpu在执行别的线程。当冻结的线程被唤醒了他最初会到阻塞状态,再判断CPU是否空闲,空闲时到运行状态
线程运行状态图:
创建线程的两种方法:
1:
class <span style="font-family: Arial;">ThreadTest </span>extends Thread
{public void run(){
线程运行代码;
}
}
class test{
public static void main(String[] args){
ThreadTest threadTest = new ThreadTest();
thread.start();
} //简化为 new Thread(){public void run(){}}.start();
2:
class RunnableTest implements Runnable{
public void run(){
线程运行代码;
}
}
class Test{
public static void main(String[] args){
RunnableTest runnableTest = new RunnableTest();
Thread thread = new Thread(runnableTest);
thread.start();
}
简化代码new Thread(new Runnable(){public void run(){}}).start();
获取当前线程的名字:Thread.currentThread().getName()
现在有个需求,设计一个买票程序,多个窗口共同买票
分析:多个窗口买票,创建多个线程,且票是共享数据,应该被static修饰。需要考虑线程同步问题。而本程序应为共享的的ticket 所以不用static修饰也可
class Ticket implements Runnable{
/*private Object obj = new Object();*/
/*定义票数据,因为创建的是一个对象资源,所以保证了数据的共享,不用static也可以*/
private int tick =100;
public void run(){
while(true){
/*如果还有票就打印下几号客户在买票,然后卖出去一张票,减一张票*/
synchronized(this){
if(tick>0){
try{
Thread.sleep(10);
/*由于考虑到如果客户有可能操作慢而导致延迟,所以加入线程延迟进行测试,我们是程序员必须要做到一些错误的排除,当然不能保证到万无一失,但也要做到尽量避免发生错误*/
}
catch(Exception e){
}
System.out.println(tick+"号客户买票\n"+Thread.currentThread().getName()+"为"+tick+"号客户服务\t...卖第"+tick--+"票");
}
/*如果没有票了,就向客户致敬,然后跳出return跳出循环,方法结束*/
else{
System.out.println(Thread.currentThread().getName()+":哥们票卖完了,打烊了");
return;
}
}
}
}
}
class Test{
public static void main(String[] args){
/*
这时要设置线程的名字的话,就要用Thread类的方法了,因为Runnable是
父接口,而设置线程的方法是在Thread类中的,所以可以直接用Thread对象调用。
*/
Ticket t = new Ticket();
Thread t1 = new Thread (t);
Thread t2 = new Thread (t);
Thread t3 = new Thread (t);
Thread t4 = new Thread (t);
t1.setName("1号窗口");
t2.setName("2号窗口");
t3.setName("3号窗口");
t4.setName("4号窗口");
t1.start();
t2.start();
t3.start();
t4.start();
/*创建线程对象,把资源当成参数进行传递(保证了资源的唯一),调用Thread类的start()方法。*/
}
}
我们可以看到 用到了代码同步,同步代码块 synchronized(唯一对象){}:同步要求,必须有两个线程或以上,且共享同一个数据
代码同步:
由于线程同步代码中可能嵌套同步,最容易导致的问题就是死锁。程序就停在那里不动了。
死锁:
死锁代码:
class MyLock {
<span style="white-space:pre"> </span>public static Object locka = new Object();
<span style="white-space:pre"> </span>public static Object lockb = new Object();
}
class DeadLockTest implements Runnable {
<span style="white-space:pre"> </span>private boolean flag;
<span style="white-space:pre"> </span>DeadLockTest(boolean flag) {
<span style="white-space:pre"> </span>this.flag = flag;
}
public void run() {
<span style="white-space:pre"> </span>if (flag) {
<span style="white-space:pre"> </span>while(true) {
<span style="white-space:pre"> </span>synchronized (MyLock.locka) {
<span style="white-space:pre"> </span>System.out.println(Thread.currentThread().getName()
<span style="white-space:pre"> </span>+"...if locka ");
<span style="white-space:pre"> </span>synchronized (MyLock.lockb) {
System.out.println(Thread.currentThread().getName() +"..if lockb");
}
}
}
}
else {
while (true) {
synchronized (MyLock.lockb) {
System.out.println(Thread.currentThread().getName()
+"..else lockb");
synchronized(MyLock.locka) {
System.out.println(Thread.currentThread().getName()
+".....else locka");
}
}
}
}
}
}
class Test {
public static void main(String[] args) {
Thread t1 = new Thread(new DeadLockTest(true));
Thread t2 = new Thread(new DeadLockTest(false));
t1.start();
t2.start();
}
}
2 线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同(一个是wait 一个是notify)。
首先我们需要知道wait() sleep() 有什么区别?
wait() :释放资源,释放锁 。
sleep() :释放资源,不释放锁。
class Res{
private String name;//定义了一个名字,用于把传递进行的名字赋值给name变量
private String user;//定义了一个信息,用于把传递进来的内容赋值给user变量
private boolean flag;//为了线程的通讯,定义了一个标记,默认值是false
private int count = 100;//定义了一个共享数据,由于资源是唯一的,所以不用被static修饰
/*对函数进行同步,使每次传递进行的数据只能存放一个,当然如果不存放数据的话,那么将无法继续取走*/
private Res(){}//为了保证资源的唯一性,所以使用了单例设计模式
private static Res res =null;//懒汉式,对象的延迟加载,你懂的大笑
public static Res getInstanceof(){
/*加入多重判断提高效率*/
if(res==null)
/*使用同步技术,加了一个锁,保证只能有一个线程能进去*/
synchronized(Res.class){
/*再次判断,如果引用变量值为空,就在本类中创建本类对象*/
if(res==null)
res = new Res();
}
/*返回这个类的本类对象*/
return res;
}
public synchronized void input(String name,String user){/*接受2个参数,一个是name,一个是user*/
while(flag){
/*判断标记是否为true,如果为true就wait,因为里面还有数据,无法继续存放*/
try{wait();}catch(Exception e){}
}
/*判断下要循环的共享数据数是否为0,为0则不进行赋值运算,不为了则进行赋值运算*/
if(count>0){
//把传递进行的name赋值给成员变量的name
this.name = name;
//把传递进来的user赋值给成员变量的user
this.user = user;
//为了线程的通讯,把标志改为真。
flag = true;
//唤醒所有正在等待的线程
/*
这里如果写成notify可以吗?答案是不可以的,因为那样就可能会产生死锁。
为什么会产生死锁呢?
因为就跟你们在看我博客的人一样,你们很多人可以一起看我的博客,
但是我写博客的时候就苦逼了,我只能一个人慢慢写。
*/
notifyAll();
//打印当前线程的名称和公司和用户和共享数据还有多少个用户要存储
System.out.println(Thread.currentThread().getName()+"...公司:"+name+"...用户..."+user+"..."+"已存入数据库...还有"+count+"客户需要存储");
}
else{
/*
如果共享数据的值(也就是客户的数量)为0了,那么将虚拟机退出让程序停掉(工程被你干完了,那么就可以下班了)。
*/
System.out.println("数据库已经满了,如果想要扩容,请付钱");/*这是我们想要的效果大笑*/
System.exit(0);
}
}
/*对函数进行同步,跟input函数进行互斥,使每次取出数据的时候只能取走input存放的数据(当然数据只有一个)
当然如果数据不取空的话,那么将无法继续存放*/
public synchronized void output(){//不用接受参数,因为是拿数据嘛
while(!flag){
/*判断标记是否为true,如果为true就wait,因为里面没有数据,无法继续取出*/
try{wait();}catch(Exception e){}
}
/*打印取出数据的线程名称和公司名称和客户和剩下多少个客户*/
System.out.println(Thread.currentThread().getName()+"..获得的数据是...公司:" +name + "用户:"+ user+"里面还有"+count--+"个用户");
//为了线程的互斥,把标志改为假。
flag = false;
/*唤醒一个正在等待的线程。因为拿东西的时候可以一个人拿而存东西的不行,只能一个一个存。
看博客 写博客的例子。
*/
notify();
}
}
/*要实现多线程嘛,所以定义一个线程类实现Runnable接口,把资源的数据存进去,达到多线程的效果*/
class Input implements Runnable{
/*把构造参数传递进行的资源对象赋值给成员变量,使资源对象作用于整个类*/
private Res r;
/*
构造的时候,为了存取操作不错乱,这时就要保证资源的唯一性,把资源对象构造时进行参数传递进来。
*/
Input(Res r){
/*把资源对象的引用赋值给成员变量。*/
this.r = r;
}
/*覆盖Runnable接口的run方法*/
public void run(){
/*定义一个变量,使每次传递进去的资源对象数据实现切换的效果*/
int x = 0;
while(true){
/*为了程序更严谨,加入了延迟,因为客户的电脑在网络上操作的时候,有使也会卡的嘛(简单的说就是延迟吧,)*/
try{Thread.sleep(20);}catch(Exception e){}
/*如果变量x=0就存入"优快云社区","优快云——新长城"*/
if(x==0)
/*调用资源对象存数据的方法,传递俩个参数,一个是公司名称,一个是客户*/
r.input("优快云社区","优快云——新长城");
/*否则就存入"黑马程序员","黑马程序员——Xcc"。这样就不放真实姓名了*/
else
r.input("黑马程序员","黑马程序员——Xcc");
/*对x的值进行操作,x永远只有俩个结果,一个是0,一个是1,当然这里你可以定义布尔型的值的,也是可以实现切换的效果的*/
x=(x+1)%2;
}
}
}
/*要实现多线程嘛,所以定义一个线程类实现Runnable接口,把存进去的资源数据取出来,达到多线程的效果*/
class Output implements Runnable{
/*线程之间的互斥,你懂的嘛!必须是同一个资源嘛!就像我写的博客 我写也是这个博客,你们看也是这个博客,当我更新博客的时候,如果你们关注我,当然我就会互斥,也就是发送消息给你们的嘛。说到底也就是一个看博客,一个写博客。都是这个博客嘛*/
/*把构造参数传递进行的资源对象赋值给成员变量,使资源对象作用于整个类*/
private Res r;
Output(Res r){
/*把资源对象的引用赋值给成员变量。*/
this.r = r;
}
/*覆盖Runnable接口的run方法*/
public void run(){
while(true){
/*这我就不说了,说了就是滴答了!跟存的注释是一样的 还是简单说一下把,就是我取数据的时候,我电脑可能网速有点慢,就卡了一下。不过这是每次都卡一下的哦。大笑你懂的偷笑*/
try{Thread.sleep(20);}catch(Exception e){}
/*调用资源对象取的方法,获取存入的数据*/
r.output();
}
}
}
/*对工程进行测试*/
public class InputOutputTest{
/*主函数*/
public static void main(String[] args){
/*由于这个类的构造函数被私有化,如果这个类应该有提供一个获取本类对象的静态方法getInstanceof()*/
Res r = Res.getInstanceof();
/*创建了一个线程,并把线程要运行的代码已经封装的Runnable子类的run方法中当成参数进行传递,调用start方法,开启线程掉用线程的run方法。由于采用了多线程技术,所以就多搞几个线程,玩玩(难得学了多线程嘛)*/
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}