线程学习总结
线程学习总结
一、线程与进程
进程:
就是正在运行的程序
进程是系统进行资源分配和调用的独立单位。 每一个进程都有自己的内存空间和系统资源
线程:
在同一个进程内又可以执行多个任务,而这每一个任务我们就可以认为是一个线程
线程:是程序的执行单元,执行路径。是程序使用CPU的最基本的单位
单线程:程序只有一条执行路径。
多线程:程序有多条执行路径。
线程的创建
线程的创建有两种方式。
1.继承Thread类
步骤:
1.自定义一个类去继承于Thread
2.在自定义类重写run()方法
为什么是run()方法:run()才是线程运行的核心方法
不是类中的所有代码都需要被线程所执行。
而这个时候,为了区分哪些代码能被线程所执行,java提供了Thread类中的run()用来包含那些线程执行的代码
例子:
public class ThreadTest extends Thread{
private String name;
public ThreadTest(String name) {
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
System.out.println(name+i);
}
}
}
public class Test1 {
public static void main(String[] args) {
ThreadTest t = new ThreadTest("琪琪");
ThreadTest t2 = new ThreadTest("呵呵");
ThreadTest t3 = new ThreadTest("ergou");
t.start();
t2.start();
t3.start();
}
}
2.实现Runnable接口
步骤:
A.自定义类MyRunnable实现Runnable接口
B.重写run()方法
C.创建MyRunnable类的对象
D.创建Thread类的对象,并把步骤C的对象作为构造参数传递
例子:
public class MyRunnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my, "李四");
t1.start();
Thread t2 = new Thread(my, "张三");
t2.start();
}
}
二、线程的优先级
-
目前我们的线程是没有优先级的,肯定是有一个默认优先级,
那么,默认优先级是多少呢?
如何获取线程对象的优先级呢?
public final int getPriority():返回线程对象的优先级
如何设置线程对象的优先级呢?public final void setPriority(int newPriority):更改此线程的优先级。
-
注意:
线程的默认优先级:5
线程优先级的范围是:1-10
线程优先级高仅仅表示线程获取CPU时间片的几率高,但是要在次数比较多,或者多运行的时候才能看到比较效果。
三、线程的礼让
- public static void yield():暂停当前正在执行的线程对象,并执行其他的线程。
让多个线程的执行更加和谐,但是不能保证一人一次
四、线程安全
线程安全:由于统一进程下的多个线程是共享同样的地址空间和数据的,又由于线程执行顺序的不可预知性,一个线程可能会修改其他线程正在使用的变量,所有由此产生了线程安全的问题了。那么我们该怎么解决这个问题呢?
基本思想:让程序间没有安全问题的环境
怎么实现呢?
把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
(把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行)。
Java给我们提供了:同步机制。
同步代码块:
synchronized(对象){
//需要同步的代码
}
A:对象是什么呢?
我们可以随便创建一个对象试试
B:需要同步的代码是哪些呢?
把多条语句操作共享数据的代码的部分包起来
注意:
同步可以解决线程安全问题的根本原因就在那个对象上,该对象如同锁的功能。
多个线程必须是同一把锁。
同步的特点:
- 前提:
多个线程 - 解决问题的时候需要注意:
多个线程使用是同一个锁对象。 - 同步的好处:
同步的出现可以解决了多线程的安全问题 - 同步的缺点:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很浪费资源的,无形中会降低了程序的运行效率。
例子:三个窗口同时售100张票。
public class SellTicket implements Runnable{
private int tickes=1000;
private Object obj = new Object();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized(this){
if(tickes>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickes--)+"张票");
}else{
break;
}
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
SellTicket ss = new SellTicket();
Thread t1 = new Thread(ss, "窗口1");
Thread t2 = new Thread(ss, "窗口2");
Thread t3 = new Thread(ss, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
五、Lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到
它哪里上了锁和哪里释放了锁。
为了更清晰的表达如何加锁和放锁,JDK5以后提供了一个新的锁对象Lock。
Lock:
void lock():获取锁 上锁
void unlock():释放锁 放锁
例子:还是上面售票为例
public class SellTicket implements Runnable{
private int tickes=100;
//因为static所修饰的变量 只会初始化一次。
//定义一个锁对象
private Lock lock = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try {
lock.lock();
if(tickes>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickes--)+"张票");
}else{
break;
}
} finally {
// TODO: handle finally clause
lock.unlock();
}
}
}
}
public class SellTickerDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
SellTicket ss = new SellTicket();
Thread t1 = new Thread(ss, "窗口1");
Thread t2 = new Thread(ss, "窗口2");
Thread t3 = new Thread(ss, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
- 分析:生产与消费
- 资源类:Phone
- 生产类:SetPhone (生产者)
- 消费类:GetPhone (消费者)
- 测试类:PhoneDemo
结果输出:null,0
-
为什么会出现这种问题呢?
原因:我们在每个线程都创建了新的资源,而我们要求的时候设置和获取的资源应该同一个。 -
如何解决这个问题呢?
用到线程同步 -
新的问题:虽然现在线程安全了,但是呢,现在是一次一大片不要看,我就想依次的一次一个的输出 Thread.sleep()肯定是不行的
-
如何实现呢?
通过Java提供的等待唤醒的机制去解决 -
等待唤醒:
类中提供了三个方法:
wait();等待
notify();唤醒单个线程
notifyAll();唤醒所有线程 -
为什么这些方法不定义Thread类中呢?
这些方法的调用必须通过锁对象来调用,而我们之前所使用的锁对象都是任意的。
所以,这些方法必须定义在Object。
线程1:将数据进行锁定 p
解除p
线程2: 线程1和2去抢占资源
利用等待唤醒机制:
其实就是为了防止的数据大批量重复出现。
我的SetPhone和GetPhone的锁对象都是Phone,那么我们又学过了Java三大特性,从而可以知道我们是可以抽取“公因式”
最终版本:
把Phone的成员变量进行私有化,
把设置和获取的操作给封装为功能到Phone,并加上同步
设置或获取的线程里只需要调用同步方法即可。
代码如下:
/*生产者*/
public class SetPhone implements Runnable{
private Phone p;
private int x;
public SetPhone(Phone p){
this.p =p;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
if(x%2==0){
p.set("华为", 2600);
}else{
p.set("魅族", 1100);
}
x++;
}
}
}
/*消费者*/
public class GetPhone implements Runnable{
private Phone p;
public GetPhone(Phone p){
this.p = p;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
Phone phone = p.get();
System.out.println(p.getName()+","+p.getPrice());
}
}
}
/*手机*/
public class Phone {
private int bianhao;
private String name;
private int price;
private boolean flag;//默认情况是没数据,如果是true,说明有数据
public int getBianhao() {
return bianhao;
}
public void setBianhao(int bianhao) {
this.bianhao = bianhao;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
//设置的方法
public synchronized void set(String name,int price){
if(this.flag){//false(不走) //false
try {
this.wait();//线程1等着,释放锁
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.price =price;
this.flag = true;
this.notify();
}
//获取的方法
public synchronized Phone get(){
if(!this.flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.flag =false;
this.notify();
return this;
}
}
/*测试类*/
public class PhoneDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Phone p = new Phone();
SetPhone sp = new SetPhone(p);
GetPhone gp = new GetPhone(p);
Thread t1 = new Thread(sp);
Thread t2 = new Thread(gp);
t1.start();
t2.start();
}
}