Java学习心得之线程(二)

时间:2021-09-05 16:23:22

一、线程的优先级别

        Java提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。使用下属方法获取或设置线程优先级:

  • int getPriority()
  • void setPriority(int newPriority)
示例:
public class TestThread6 {
public static void main(String args[]) {
MyThread4 t4 = new MyThread4();
MyThread5 t5 = new MyThread5();
Thread t1 = new Thread(t4);
Thread t2 = new Thread(t5);
t1.setPriority(Thread.NORM_PRIORITY + 3);// 使用setPriority()方法设置线程的优先级别,这里把t1线程的优先级别进行设置
/*
* 把线程t1的优先级(priority)在正常优先级(NORM_PRIORITY)的基础上再提高3级
* 这样t1的执行一次的时间就会比t2的多很多     
* 默认情况下NORM_PRIORITY的值为5
*/
t1.start();
t2.start();
System.out.println("t1线程的优先级是:" + t1.getPriority());
// 使用getPriority()方法取得线程的优先级别,打印出t1的优先级别为8
}
}

class MyThread4 implements Runnable {
public void run() {
for (int i = 0; i <= 1000; i++) {
System.out.println("T1:" + i);
}
}
}

class MyThread5 implements Runnable {
public void run() {
for (int i = 0; i <= 1000; i++) {
System.out.println("===============T2:" + i);
}
}
}

输出结果:
T1:0
T1:1
===============T2:0
===============T2:1
t1线程的优先级是:8
===============T2:2
T1:2
T1:3
===============T2:3
T1:4
T1:5
===============T2:4
T1:6
T1:7
===============T2:5

t1线程分配的cpu时间会比t2线程时间多。

二、线程同步
测试代码:
public class TestSync implements Runnable{
private Timer timer = new Timer();
public static void main(String[] args) {
TestSync test = new TestSync();
/*
* 开始10条线程,每条线程执行循环100000次,总共执行1000000次
* 那么最终打印结果是100000,然而并非每次都如此。
*/
for(int i=0;i<10;i++){
Thread t = new Thread(test);
t.setName("线程"+i);
t.start();
}
}
@Override
public void run() {
/*
* 一个线程中,循环100000次,每次num加1
*/
for(int i=0;i<100000;i++){
timer.add();
}
}
}
class Timer{
private static int num = 0;
public void add(){
num++;
System.out.println(num);
}
}

输出结果:
999993
999994
999995
999996
999997
999998
999999

      多执行几次上述代码,我们发现不是每次执行结果都是正确的(多执行几次),这是为什么呢?
这里的num变量是共享的,如果有两个线程同时执行num++的时候,并且两个线程获得num(假设此时num=100),那么两个线程操作完之后,结果都是num=101。因此从最终结果上看,就是少加了一次,当然也可能少加2次或更多。
       如何解决这个问题呢?
       在Java语言中,引入对象互斥锁的概念,保证共享数据操作的完整性。没个对象都对应于一个可称为“互斥锁”的标记,这个标记保证在任意时刻,只能有一个线程访问该对象。关键字synchronized来与对象的互斥锁联系。当某个对象synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
public class TestSync implements Runnable{
private Timer timer = new Timer();
public static void main(String[] args) {
TestSync test = new TestSync();
/*
* 开始10条线程,每条线程执行循环100000次,总共执行1000000次
* 那么最终打印结果是100000,然而并非每次都如此。
*/
for(int i=0;i<10;i++){
Thread t = new Thread(test);
t.setName("线程"+i);
t.start();
}
}
@Override
public void run() {
/*
* 一个线程中,循环100000次,每次num加1
*/
for(int i=0;i<100000;i++){
timer.add();
}
}
}
class Timer{
private static int num = 0;
// 在声明方法时加入synchronized时表示在执行这个方法的过程之中当前对象被锁定
public /*synchronized*/ void add(){
/*
* 使用synchronized(this)来锁定当前对象,这样就不会再出现两个不同的线程同时访问同一个对象资源的问题了 只有当一个线程访问结束后才会轮到下一个线程来访问
*/
synchronized (this) {
num++;
System.out.println(num);
}
}
}

死锁
public class SyncDeadLock {
private static Object locka = new Object();
private static Object lockb = new Object();

public void deadLock(){
Thread thread1 = new Thread(new Runnable(){
@Override
public void run() {
//获取锁locka
synchronized (locka) {
try {
System.out.println(Thread.currentThread().getName()+"获取锁a");
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"休眠了500毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"等待获取锁b!");
//试图获取lockb,但是lockb已经被thread2锁定
synchronized (lockb){
System.out.println(Thread.currentThread().getName()+" 已获取锁b!");
}
}
}
},"thread1");
Thread thread2 = new Thread(new Runnable(){

@Override
public void run() {
//获取锁lockb
synchronized (lockb) {
try {
System.out.println(Thread.currentThread().getName()+"获取锁b");
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"休眠了500毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"等待获取锁a!");
//试图获取locka,但是locka已经被thread1锁定
synchronized (locka){
System.out.println(Thread.currentThread().getName()+" 已获取锁a!");
}
}
}

},"thread2");
thread1.start();
thread2.start();
}

public static void main(String[] args) {
new SyncDeadLock().deadLock();
}
}

输出结果:
thread2获取锁b
thread1获取锁a
thread2休眠了500毫秒
thread2等待获取锁a!
thread1休眠了500毫秒
thread1等待获取锁b!

       上述代码中,我们看到thread1获取锁locka后想再获取lockb,但是lockb已经被thread2锁定,thread2获取lockb的同时想获取locka,locka也被thread1锁定,这样就很矛盾,thread1只有获取lockb后才能往下执行,thread2只有获取locka后才能往下执行,但是两个人都不放弃,因此最后进入死锁状态,程序无法执行下去。解决线程死锁的问题最好只锁定一个对象,不要同时锁定两个对象。

生产者消费者问题:
/*    范例名称:生产者--消费者问题
* 源文件名称:ProducerConsumer.java
* 要 点:
* 1. 共享数据的不一致性/临界资源的保护
* 2. Java对象锁的概念
* 3. synchronized关键字/wait()及notify()方法
*/
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack stack = new SyncStack();
Runnable p=new Producer(stack);
Runnable c = new Consumer(stack);
Thread p1 = new Thread(p);
Thread c1 = new Thread(c);

p1.start();
c1.start();
}
}
class SyncStack{//支持多线程同步操作的堆栈的实现
private int index = 0;
private char [] data = new char[6];
//储存栈
public synchronized void push(char c){
/*
* 如果栈满,那么线程进入阻塞
*/
if(index == data.length){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.notify();
data[index] = c;
index++;
}
//取出栈
public synchronized char pop(){
/*
* 如果栈为空,那么线程进入阻塞
*/
if(index==0){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.notify();
index--;
return data[index];
}
}
/*
* 生产者:
*
*/
class Producer implements Runnable{
SyncStack stack;
public Producer(SyncStack s) {
stack = s;
}

@Override
public void run() {
for(int i=0; i<20; i++){
//随即获取字母
char c =(char)(Math.random()*26+'A');
//将字母推进栈中
stack.push(c);
System.out.println("produced:"+c);
try{
Thread.sleep((int)(Math.random()*1000));
}catch(InterruptedException e){

}
}

}

}
/*
* 消费者:
*
*/
class Consumer implements Runnable{
SyncStack stack;
public Consumer(SyncStack s){
stack = s;
}
@Override
public void run() {
for(int i=0;i<20;i++){
//字母取出栈
char c = stack.pop();
System.out.println("消费:"+c);
try{
Thread.sleep((int)(Math.random()*1000));
}catch(InterruptedException e){

}
}
}

}