1、线程的定义
单道程序设计:计算机中除了操作系统之外,只存在一个用户程序,即用户独享整个计算机的资源
特点:资源独占性:任何时候,位于内存中的程序可以使用系统中的一切资源,不可能有其他程序的竞争;执行的顺序性:内存中每次只有一个程序,各个程序按次序执行;结果的再现性:只要执行环境和初始条件相同,重复执行一个程序结果都一样。
多道程序设计::计算机中除了操作系统之外,存在多个用户程序,这些程序同时运行。
特点:间断性,因为资源的共享和合作,并发程序之间相互制约,造成合作执行的间断;失去封闭性:程序执行受外界影响;不可再现性:程序重复执行可能得不到相同的结果(同一时刻运行程序多)。
进程:一次运行一个程序,程序即程序的运行。现在程序将不能模拟程序的运行。一个程序被三个程序同时运行加载,一个程序产生三个进程。用进程来模拟加载数据之后的一次运行。
线程: 是一个程序里的不同执行路径,单个程序内部是可以在同一时刻进行多种运算的.
(1)什么是进程?为什么需要进程?
(2)**什么是线程?**为什么需要线程?(进程之间的切换需要花时间,耗时)
多线程优势:编程简单,效率高,适合于开发服务
class A extends Thread
{
public void run()
{ while(true){
System.out.println("AAAA");
}
}
}
public class TestThread_2
{
public static void main(String[] args)
{
A aa = new A();
aa.start(); //不同于run();start()开启一个新线程,上面必须含run()方法执行“AAA”,交替同时执行下面的代码,在某个结点分支,多代码交替的占用cpu,会自动调用run方法aa.run()只有“AAA”执行
System.out.println("BBBB");
}
}
运行结果:
2、线程的创建
第一种方式创建:
注意:一个线程只能执行run方法中的代码
线程状态的切换
一个thread对象不能调用两次start()
不能两次调用start()
package java_part;
class ThreadA extends Thread
{
public void run()
{
System.out.println("AAAA");
/*
while (true) //第二个aa.start()没有被执行到,一直输出是AAA
{
System.out.println("AAAA");
}
*/
}
}
public class TestThread_1
{
public static void main(String[] args)
{
ThreadA aa = new ThreadA();
aa.start(); //aa.start(); 会自动调用run方法
aa.start();//
//aa.start();
while (true)
{
System.out.println("BBBB");
}
}
}
第二种创建方式
利用接口实现
class ThreadB implements Runnable{
public void run() {
while(true) {
System.out.println("AAA\n");
}
}
}
public class TestThread_2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadB bb=new ThreadB();
Thread t=new Thread(bb);
t.start();
while(true) {
System.out.println("BBB\n");
}
//里面没有start()方法bb.start();
}
}
Thread中的常用方法
public final void setName(String name)
设置当前线程的名字
public static Thread currentThread()
返回当前正在执行的线程对象的引用
public final String getName()
返回当前线程的名字
class ThreadC extends Thread{
public void run() {
System.out.println("AAA\n");
System.out.printf("%s在执行\n",Thread.currentThread().getName());
}
}
public class ThreadFun {
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadC cc1=new ThreadC();
cc1.start();
cc1.setName("What");
ThreadC cc2=new ThreadC();
cc2.start();
cc2.setName("How");
ThreadC cc3=new ThreadC();
cc3.start();
System.out.println("CCCC\n");
System.out.printf("%s在执行\n",Thread.currentThread().getName());//Thread.currentThread()返回当前对象的引用
}
}
实验结果:
3、线程的控制
(1)方法如下
Thread.sleep()将当前线程睡眠。阻塞,下一时刻不能立即运行,需要进入就绪状态
join():等待该线程结束,再回复当前线程的运行
yield():(本来在运行)让出cpu,当前线程进入就绪队列等待调度,下一时刻可以立即运行;
wait();
notify();
(2)线程的优先级1-10,默认为5,
(3)线程的休眠–暂停当前运行中的线程,使之进入阻塞状态。待经过指定的“延迟时间”后再醒来并转入就绪状态
Thread.sleep()
一般利用Thread.sleep()放置run()方法,对当前线程进行暂停。同时,无论是继承Thread类的run方法还是实现Runnable接口的run方法,都不能抛出异常。
public class TestSleep_3
{
public static void main(String[] args)
{
SleepA aa = new SleepA ();
Thread tt = new Thread(aa);
tt.start();
for (int i = 0; i < 3; i++) {
System.out.println("main thread :"+i);
}
}
}
class SleepA implements Runnable
{
public void run() //throws Exception
{ //9行
for (int i=0; i<10; ++i)
{
System.out.println(Thread.currentThread().getName() + " " + i);
try
{
Thread.sleep(1000); //让线程暂停 1s
}catch (Exception e)
{
}
//Thread.sleep(1000);
System.out.println("哈哈哈哈哈哈哈哈哈");
}
}
}
结果输出为
一直到输出为Thread-0 9
哈哈哈哈哈哈哈哈
有时也会输出:开启的Thread0线程在暂停1s中进入阻塞状态,可能不会立即执行,主要是cpu控制。
Thread.yield()线程的让步
让出cpu,给其他线程执行的机会
让运行中的线程主动放弃当前获得的cpu处理机会。但不是使该线程阻塞,而是使之转入就绪状态。public static void yield()注意:
使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了
Thread.join()
需要原因:在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。在哪个线程A中调用另一个线程B.join(),就需要等待B线程结束之后,再执行A线程。
public class TestJoin {
public static void main(String args[]){
MyRunner r = new MyRunner();
Thread t = new Thread(r);
t.start();
try{
t.join(); //7行 暂停当前正在执行t.join();的线程,直到t所对应的线程运行终止之后,当前线程才会获得继续执行的机会
}catch(InterruptedException e){
e.printStackTrace();
}
for(int i=0;i<50;i++){
System.out.println("主线程:" + i);
}
}
}
class MyRunner implements Runnable {
public void run() {
for(int i=0;i<50;i++) {
System.out.println("子线程: " + i);
}
}
}
结果输出:
什么是死锁:两个线程彼此都需要对方资源进行运行,但双方都不想让,程序无法运行。
4、线程的同步
A买票
同步:synchronized这个关键字,将任务锁起来,当某个线程进来时,不能让其他线程继续进来,那就代表是同步了。A线程在执行时,另一个线程不能执行。直到执行完其他线程才可以执行。
同一个站点共同对100张票卖,多个线程对同一段关键程序资源进行操作。
synchronized修饰一个方法或者一个方法内部代码
重点:synchronized修饰代码块,
synchronize(类对象名aa)//判断aa是否已被其他线程霸占,若被其他线程霸占,当前线程陷入等待,否则当前占用,指向同步代码块,其他线程没有可能再执行。当前线程执行我那同步代码块之后释放对aa对象的霸占,此时,其他线程会相互经常对aa的霸占,最终cpu选择其中一个线程执行。
{
同步代码块
}
(1)利用实现接口方法,以及synchronized(str){},public int tickets,因为使用相同的对象,变量ticket和String不需要设置静态变量
public class TestSyn
{
public static void main(String[] args) throws Exception
{
SynA aa = new SynA();
Thread t1 = new Thread(aa);
Thread t2 = new Thread(aa);
t1.start();
t2.start();
}
}
class SynA implements Runnable
{
public int tickets = 100;
String str = new String("123");
public void run()
{ //String str = new String("123");将不是锁定同一个对象
while (true)
{synchronized (str) {
if (tickets > 0)
{
System.out.printf("%s线程正在卖出第%d张票!\n",
Thread.currentThread().getName(), tickets);
--tickets;
}
else
{
break;
}
}
}
}
}
对于容易出错的利用synchronized(this)
public class TestSync3
{
public static void main(String[] args)
{
ThreadRun tr = new ThreadRun();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
}
}
class ThreadRun implements Runnable
{
private int tickets = 100;
public void run()
{
synchronized (this)
{
while (true)
{
if (tickets > 0)
{
System.out.printf("%s线程正在卖出第%d张票!\n",
Thread.currentThread().getName(), tickets);
--tickets;
}
else
{
break;
}
}
}
}
}
2、利用继承extends实现同步
/ok注意用生成不同的对象,保证变量为静态,锁定同一个对象。
class A extends Thread
{
public static int tickets = 100; //static不能省
public static String str = new String("哈哈"); //static不能省
public void run()
{
while (true)
{
synchronized (str)
{
if (tickets > 0)
{
System.out.printf("%s线程正在卖出第%d张票\n",
Thread.currentThread().getName(), tickets);
--tickets;
}
else
{
break;
}
}
}
}
}
public class TestTickets_3
{
public static void main(String[] args)
{
A aa1 = new A();
aa1.start();
A aa2 = new A();
aa2.start();
}
}
3利用synchronized修饰函数
class Thd2 implements Runnable
{
private int tickets = 100;
public synchronized void run()
{
while (true)
{
if (tickets > 0)
{
System.out.printf("%s线程正在卖出第%d张票!\n",
Thread.currentThread().getName(), tickets);
--tickets;
}
else
{
break;
}
}
}
}
public class TestSync
{
public static void main(String[] args)
{
Thd2 tr = new Thd2();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
new Thread(tr).start();
}
}
B生产消费
(1)
this.notify:不是叫醒正在执行this.notify()的当前线程,而是叫醒一个现在wait this对象的其他线程,(若另外线程因为执行this.wait阻塞的线程)如果多个线程正在wait this对象,通常叫醒最先wait this对象的线程,但具体叫醒哪一个通常由系统调度器控制,程序员无法控制。所谓叫醒就是令该线程性因为wait陷入阻塞转入就绪状态。
this.wait线程暂停:将执行this.wait()当前线程转入阻塞状态,释放对对象的锁定。
对象.notifyAll()叫醒其他所有因为执行对象.wait()而陷入阻塞状态的线程。
《经典消费问题》仓库最多容纳6个产品,制造商要制造10件产品放入仓库,消费者从仓库中取出20件产品来消费。制造商制造商品和消费者取出产品的速度可能不一样。
public class SyncTest
{
public static void main(String args[]) throws Exception
{
SyncStack stack = new SyncStack();
Runnable p=new Producer(stack);
Runnable c = new Consumer(stack);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
Thread.sleep(1000);
t2.start();
// Thread t3 = new Thread(p);
// t3.start();
// Thread t4 = new Thread(c);
// t4.start();
}
}
class SyncStack{ //支持多线程同步操作的堆栈的实现
private int index = 0;
private char []data = new char[6];
public synchronized void push(char c)
{
while(index == data.length)//满了,暂停
{
try
{
this.wait(); //暂停
}catch(InterruptedException e)
{
e.printStackTrace();
System.exit(-1);
}
}
this.notify();
data[index] = c;
index++;
System.out.println("生产: "+c);
System.out.printf("容器里有%d个字母\n\n", index);
}
public synchronized char pop()
{
while(index == 0)
{
try
{
this.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
System.exit(-1);
}
}
this.notify();
index--;
System.out.println("取出:"+data[index]);
System.out.printf("容器里还有%d个字母!\n\n", index);
return data[index];
}
}
class Producer implements Runnable
{
SyncStack stack;
public Producer(SyncStack s)
{
stack = s;
}
public void run()
{
for(int i=0; i<20; i++)
{
char c =(char)('a'+i);
stack.push(c);
try
{
Thread.sleep((int)(Math.random()*20));
}catch(InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
SyncStack stack;
public Consumer(SyncStack s)
{
stack = s;
}
public void run()
{
for(int i=0;i<20;i++)
{
char c = stack.pop();
try
{
Thread.sleep((int)(Math.random()*1000));
}
catch(InterruptedException e)
{
}
}
}
}
实验结果
生产线程正在生产第1个产品,该产品是: a
生产线程正在生产第2个产品,该产品是: b
生产线程正在生产第3个产品,该产品是: c
生产线程正在生产第4个产品,该产品是: d
生产线程正在生产第5个产品,该产品是: e
生产线程正在生产第6个产品,该产品是: f
消费线程正在消费第6个产品,该产品是: f
生产线程正在生产第6个产品,该产品是: g
消费线程正在消费第6个产品,该产品是: g
生产线程正在生产第6个产品,该产品是: h
消费线程正在消费第6个产品,该产品是: h
生产线程正在生产第6个产品,该产品是: i
消费线程正在消费第6个产品,该产品是: i
生产线程正在生产第6个产品,该产品是: j
消费线程正在消费第6个产品,该产品是: j
生产线程正在生产第6个产品,该产品是: k
消费线程正在消费第6个产品,该产品是: k
生产线程正在生产第6个产品,该产品是: l
消费线程正在消费第6个产品,该产品是: l
生产线程正在生产第6个产品,该产品是: m
消费线程正在消费第6个产品,该产品是: m
生产线程正在生产第6个产品,该产品是: n
消费线程正在消费第6个产品,该产品是: n
生产线程正在生产第6个产品,该产品是: o
消费线程正在消费第6个产品,该产品是: o
生产线程正在生产第6个产品,该产品是: p
消费线程正在消费第6个产品,该产品是: p
生产线程正在生产第6个产品,该产品是: q
消费线程正在消费第6个产品,该产品是: q
生产线程正在生产第6个产品,该产品是: r
消费线程正在消费第6个产品,该产品是: r
生产线程正在生产第6个产品,该产品是: s
消费线程正在消费第6个产品,该产品是: s
生产线程正在生产第6个产品,该产品是: t
消费线程正在消费第6个产品,该产品是: t