黑马程序员---线程1
来源:互联网 发布:python基础教程怎么样 编辑:程序博客网 时间:2024/06/07 15:18
----------------------------------------android培训 java培训 期待与您交流-------------------------------------------------
(1).进程与线程
进程:是一个正在执行的程序,每个进程执行都有自己的执行顺序,这就叫程序的执行路径
线程:就是进程中独立的控制单元,一个进程中可以有多个线程,这些线程共享进程的内存,线程控制进程的执行
一个进程中至少有一个线程
多线程之间互不干扰,一个之间出现异常不影响另一个线程的运行,它们各自都有自己的栈,各自管理自己的栈
JVM在启动时会创建一个线程,这个线程执行main()方法中的代码,称为主线程
(2).创建线程的方法:
第一种方式:继承Thread类
1:定义类继承Thread类
2: 重写Thread中的run()方法;将线程要执行的代码放入run()方法中,线程运行是就运行run()方法中的代码
3:调用start()方法;目的是启动线程,运行run()方法
getName():返回线程对象的名称
currentThread():返回正在执行的线程的名称
第二种方式:实现Runnable的接口
1.定义实现Runnable的类
2.覆盖Runnable接口中的run()方法
3.通过Thread类建立线程对象
4.将Runnable子类的对象传递给Thread类的构造函数
因为自定义的run方法是Runnable接口的子类的对象,所以要让线程指定run的方法
5.调用Thread类的start方法,开启线程
(3).实现和继承方法的区别:
实现:避免了单继承的局限性,建立线程时建议使用这种方式
继承方式中线程代码放在run方法的代码中,实现方式中线程代码放在实现Runnable接口的子类run()方法中
继承Thread创建线程的弊端:
无法再继承其它的类,因为继承了Thread类,java不支持多继承
因为每个线程都有一个Thread子类的实例,所以多个线程之间共享数据比较麻烦
(4).CPU小常识:
CPU执行资格:具备被CPU处理的资格,正在CPU处理的队列中排队
CPU执行权:正在被CPU执行的权力
程序执行过程:
当程序被加载进内存后,操作系统就为程序赋予了执行资格并加上了一个标记,程序在进程的执行队
列中排队,等待着操作系统执行程序;当程序运行完或系统分配给程序的时间片用完后,操作系统就会准备
就绪的执行队列中调用程序进行执行,这样这个程序就具备了执行权明确一点,在某一个时刻,只能有一个
程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果,我们可以形象把多线程的
运行行为在互相抢夺cpu的执行权
临时阻塞状态:具备执行的资格但不具备执行权,正在等待执行权
静态同步方法和非静态同步方法不会彼此阻塞,因为静态方法同步锁定在class类文件上,非静态方法锁定
在类的对象上
(4).注意点
多次启动一个线程是非法的,特别是当线程已经结束执行后,不能再重新启动,否则会抛出
IllegaThreadStateException异常
*/
//第一种创建方式:
class Demo extends Thread{
//public String name;
Demo(String name){
super(name);
//this.name=name;
}
public void run(){
for(int i=0;i<70;i++)
System.out.println("demo running……"+i+"……"+Thread.currentThread().getName());
}
}
public class ThreadDemo{
public static void main(String[] args){
Demo dd=new Demo("dd");
Demo dd1=new Demo("dd1");
dd.start();
dd1.start();
for(int i=0;i<60;i++)
System.out.println("Thread running……"+i+"……"+Thread.currentThread().getName());
}
}
//第二种创建方式:
class Demo implements Runnable{
Object obj=new Object();
public synchronized void run(){
for(int i=0;i<10;i++){
{
System.out.println("demo run……"+Thread.currentThread().getName());
System.out.println("hello");
}
System.out.println("word");
}
System.out.println("word");
}
}
public class ThreadDemo{
public static void main(String[] args){
Demo dd=new Demo();
Thread t=new Thread(dd);
Thread t1=new Thread(dd);
Thread t2=new Thread(dd);
t.start();
t1.start();
t2.start();
System.out.println(t.getName());
System.out.println(Thread.currentThread().getName());
System.out.println("word");
}
}
//线程共享数据产生的问题
class Ticket2 implements Runnable{
private String name;
private int num=100;
Ticket2(String name){
this.name=name;
}
public void show(){
if(num>0)
System.out.println(Thread.currentThread().getName()+"--"+"Ticket:"+num--);
}
public void run(){
while(true){
show();
}
}
}
public class TestTicket2{
/*
这个程序创建了三个线程,但三个线程共享一个任务对象;这样卖出的可以表示是种票
但这样仍存在不安全因素;因为线程是的执行是随机切换的,如还有一张票的时候,这时线程1刚好做完if判断
线程切换到线程2执行,这时线程2仍能仍能进入if语句里,因为线程1还没有执行num--操作,num还是在于0的;
这样当线程1再次执行时,num就为成了1,当线程2再次执行时就输出了0,这样就将0张票给卖出去了,显然卖
出0张票是不合理的
*/
public static void main(String[] args){
Ticket2 t=new Ticket2("tt");
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
(1).多线程不安全的原因:
多线程具有随机性,因为CPU在不断的快速的切换,有一个线程对多条操作共享数据的代码执行的一部分还
没有执行完另一个线程开始参与执行,就会发生数据错误。
(2).多线程的不安全前提:
线程代码是有操作共享的数据
操作共享数据的代码有多条
(3).synchronized:同步关键字
同步代码块:
synchronized(对象){
操作共享数据的代码
}
同步函数:
synchronized 函数名 {
函数代码
}
(4).线程同步的前提:必须使用同一把锁
任何线程在进入同步方法,同步代码块之前,必须先获取同步方法,同步代码块对应的同步监听器
对于同步代码块,程序必须为它显式的指定同步监听器
对于非静态的同步方法而言,该方法的同步监听器是this--即调用该方法的对象
对于静态的同步方法,该方法的同步监听器不是this,而是类本身
(5).注意点
对于并行执行同一个类的两个不同对象的同步方法并没有限制,只是控制对同一个对象的并发访问
在同步时,应该注意尽量只同步对共享数据操作的语句
(6).多线程弊端:
较为消耗资源
同步嵌套后容易发生死锁
*/
class Ticket4 implements Runnable
{
private int tick = 100;
public boolean flag=true;
Object obj = new Object(); //这只创建了一个锁,多个线程共用一把锁,能保证线程的同步
public void run()
{
//Object obj = new Object(); //这就是每创建一个线程就创建了一个锁,这并不能保证线程的同步
while(true)
synchronized(obj)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("------obj-"+Thread.currentThread().getName()+"---");
System.out.println(tick--);
}
}
//show();
}
public synchronized void show(){
if(tick>0){
System.out.println("-show-"+Thread.currentThread().getName()+"---");
System.out.println("Ticket:"+tick--);
}
}
}
class TestTicket4
{
/*
程序创建了一个任务对象,创建了四个线程任务,每个线程共用同一把锁
当线程任务中只有一个同步代码块或同步方法时,比较好理解,但当线程任务中有多个同步代码块或同步方法时
就不好理解了,这时我们要知道,每个线程都是运行run()方法中的内容,当所有的线程共用同一把锁时,它们
争得的资源也就是锁,谁先得到锁谁就可以执行,当该线程释放锁后,所有线程又会再次争取锁
*/
public static void main(String[] args)
{
Ticket4 t = new Ticket4();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
(7).死锁:同步中嵌套同步
多个线程并发执行,共同使用共享资源,会出相互拥有资源而无法用其他线程拥有的资源,又无法释放自己的资源导 致死锁
//死锁代码
class Test implements Runnable{
public boolean flag=true;
Object obj=new Object();
public void run(){
if(flag)
synchronized(obj){
while(true){
System.out.println("if--obj--");
show();
}
}
else{
while(true)
show();
}
}
public synchronized void show(){
System.out.println("else--show---");
//try{wait(30);}catch(Exception e){}
synchronized(obj){
System.out.println("show obj--");
}
}
}
public class Te{
/*
程序中创建了一个任务对象 ,两个线程任务;在同步代码中方法中嵌套了同步代码块,同步代码块中嵌套了同步
方法;当两个线程在执行时,同步代码块取得锁并等待着取同步方法中的锁,而同步方法取得锁并等待着取同步
代码块中的锁,这样两个线程都在等待着对方释放锁资源,从而造成二个线程都在等待状态,即为死锁
*/
public static void main(String[] args){
Test t=new Test();
// Test t2=new Test();
new Thread(t).start();
new Thread(t).start();
t.flag=false;
// new Thread(t2).start();
}
}
(9).线程间的通信
多个线程在处理同一个资源,但是任务却不同,多个线程之间通过某种通信方式来协调完成任务
(10).等待唤醒机制
wait():让线程处于冻结状态,被wait的线程会被存储到线程中
notity():唤醒线程池中一个线程(任意线程)
notityAll():唤醒线程池中的所有线程
以上的方法都是继承自Object对象,因为这些方法是监视器的方法,监视器其实就是锁,锁可以是任意的对象,
任意的对象调用的方式一定定义在Object当中
注意:这些方法必须定义在同步中,因为这些方法是用于操作线程状态的方法中,必须要明确到底操作的是那个锁上的线程
wait()要捕获异常,而notify(),notifyAll()这两个方法上的异常是运行时异常,不用捕获
(11).sleep()和wait()方法的区别
sleep()是Thread中的静态方法,必须要有时间限制;当使用sleep()时会释放执行权,但是不释放锁
wait()是Object中的方法,没有时间限制;当使用wait()时不仅会释放执行权还会释放锁
(12).线程常见的一些方法。
setDaemon()将线程标记为守护线程
join();等待该线程终止
优先级 可以设置线程获取cpu执行权限的优先顺序
yield();临时暂停线程
在开发时,可以使用匿名内部类来完成局部的路径开辟。
(13).Lock:出现替代了同步代码块或者同步函数,将同步的隐式锁操作变成现实锁操作同时更为灵活,可以一个锁上加
上多组监视器
JDK1.5以后出现的,该接口将锁和同步封装成了对象;并将操作锁的隐式方法定义到了该对象中,将隐式动作
变成了显示动作同时更为灵活,可以一个锁上加上多组监视器
lock():获取锁
unlock():释放锁,通常定义需要定义finally代码块中
Condition接口:出现替代了object中的wait,notify(),notifyAll方法,将这些监视器方法单独进行了封装,
变成Condition监视器对象,可以任意锁进行组合
await():让线程等待
signal():唤醒某个线程
signalAll():唤醒所有线程
(14).停止线程的方法:
stop()方法,已过时
run()方法结束: 停止线程可以使用run方法,当线程没有了要运行的代码线程就结束了,意味着,任务结束,线程消失。一般情况下run方法中会定义循环,结束run方法,通过标记控制其中的循环就行了,如果读不到标记,线程就处于了冻结状态,可以用Thread类中的interrupt方法,清除线程中的冻结状态。
/*
生产者消费者问题
等待唤醒机制
*/
class Resource{
private String name=null;
private int count=0;
private boolean flag=false;
public synchronized void set(String name){
while(flag) //这里不能用if判断,因为if只判断一次而while是循环判断
try{wait();}catch(Exception e){}
this.name=name;
count++;
System.out.println("--生产商品--"+this.name+count+Thread.currentThread().getName());
flag=true;
notifyAll();//这里不能用notify(),因为notify()只唤醒线程池中的一个线程,在这个程序中会产生死锁
}
public synchronized void out(){
while(!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"--消费--"+this.name+count);
flag=false;
notifyAll();
}
}
class Consumer implements Runnable{
public Resource r=null;
Consumer(Resource r){
this.r=r;
}
public void run(){
while(true)
r.out();
}
}
class Producer implements Runnable{
public Resource r=null;
Producer(Resource r){
this.r=r;
}
public void run(){
while(true)
r.set("+商品+");
}
}
public class Test5{
/*
要求:有多个生产者和多个消费者,生产者每生产一个商品消费者就消费一个商品
分析:有多个生产者和多个消费都,所以可以用多线程来解决;
多个线程用于生产,多个线程用于消费;因为商品是共享资源,所以我们得在对商品资源的操作上加上
synchronized以保证资源的同步性;因为是生产一个消费一个,因而我们得用一个变量来保存资源的
变化,可以用boolean变量来保存,当资源输入后boolean变为true;输出资源在判断到是true后就知道
有资源可以输出,输出资源后将变量改为false;输入资源在判断为flase后继续输入,为true则线程等待
在程序中我们得注意几个地方,一个是判断语句我们应该用while而不是if;因为while是循环判断而if
只判断一次,用if语句,在只有一个生产者和一个消费者时程序不会混乱,但在有多个生产者和消费者
时程序会发生错误;会出现生产一次消费多次或生产多次消费一次的情况;而在有多个生产者和消费者
的情况下,程序中应该用notifyAll()而不是notify();用notify()有可能产生死锁情况
*/
public static void main(String[] args){
Resource r=new Resource();
Consumer cr=new Consumer(r);
Producer pr=new Producer(r);
new Thread(pr).start();
new Thread(pr).start();
new Thread(cr).start();
new Thread(cr).start();
}
}
- 黑马程序员---线程1
- 黑马程序员 线程(1)
- 黑马程序员-java线程1
- 黑马程序员------线程1(小结)
- 黑马程序员_多线程(1) 传统线程
- 黑马程序员-Java学习06-线程1
- 黑马程序员之多线程
- 黑马程序员-----线程复习
- “黑马程序员”~线程例题
- 黑马程序员_线程
- 黑马程序员 线程总结
- 黑马程序员:线程池
- 黑马程序员-线程同步
- 黑马程序员-线程入门
- 黑马程序员----线程
- 黑马程序员-线程
- 黑马程序员-----线程
- 黑马程序员:java线程
- Mac OS Terminal 基本指令(全)
- python-str函数详解
- 关于《JavaScript高级程序设计(第2版)》中的一句话
- Bootloader的执行过程
- 黑马程序员_C#代码快速注释。
- 黑马程序员---线程1
- uva196 - Spreadsheet(电子表格)
- linux中apt-cache命令的用法
- Java面试题
- perl中\/就是‘/’
- strlen()有误的问题
- Uva-1450-Airport
- 基础算法--A一只小蜜蜂 裴波那契数列
- 插值法之Language和基本插值多项式的C++代码实现