创建线程的三种方式
要启动线程必须重写run方法
1.继承Thread类
public class serverThread extends Thread{
public String str;
//方法的重载
public serverThread(String str){
this.str=str;
public void run(){
}
}
2.实现Runnable接口。
public class ClientThread implements Runnable{
public void run(){
}
}
3.匿名类
public class NoThread implements Runnable{
Thread t1=new Thread(){
public void run(){
}
};
t1.start();
或者
new Thread(){
public void run(){
}
}.start();
}
线程的常用方法
共同点:必须使用try抓住InterruputedException异常
1.sleep(long mills):使线程睡眠给定毫秒的时间。
2.join()😕/使线程加入到main线程中来,只有线程运行结束,才会继续往下走
3.setPriority(int Priority):设置线程优先级,当线程处于竞争关系的时候,优先级高的线程会有更大的几率获得CPU资源,线程最大优先级可用Thread.MAX_PRIORITY表示 ,不过在使用了join()方法之后,setPriority()方法无效。例子:
Thread t1;t1.setPriority(Thread.MAX_PRIORITY);
4.yield():临时暂停线程,使其他线程可以参与工作
public class TestThread {
static class Hero{
String name;
int hp;
public void heroisattacked() {
this.hp--;
}
public void herocured() {
this.hp++;
}
}
public static void main(String args[]) {
Hero h=new Hero();
h.name="凯";
h.hp=1000;
Thread t1=new Thread() {
public void run() {
for(int i=0;i<100;i++) {
//1.使用sleep方法睡眠100毫秒
try{
Thread.sleep(100);
4.暂停线程
Thread.yield();
}catch(InterruptedException e) {
e.printStackTrace();
}
h.heroisattacked();
System.out.printf("英雄%s减血后剩下%d血%n",h.name,h.hp);
}
}
};
t1.start();
//2.使用Join()方法,使t1线程加入到main线程中来,只有线程运行结束,才会继续往下走,注意join()必须在strat()之后使用。
/*try {
t1.join();
}catch(InterruptedException e) {
e.printStackTrace();
}*/
Thread t1=new Thread() {
public void run() {
for(int i=0;i<100;i++) {
try{
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
h.herocured();
System.out.printf("英雄%s加血后剩下%d血%n", h.name,h.hp);
}
}
};
t2.start();
t1.setPriority(5);
t2.setPriority(5);
}
}
线程的同步
synchronized(Object someObject)方法:多个线程占有someObject对象后再使用线程方法,如果其他线程试图占有对象,就会等待,只有在线程方法结束后,才会释放someObject对象。解决了多个线程修改同一个值而产生的脏数据问题。
Object someObject=new Object();
synchronized(someObject) {
//1.使用sleep方法睡眠100毫秒
try{
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
h.heroisattacked();
}
synchronized(someObject) {
try{
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
h.herocured();
}
在方法内部加上synchronized修饰,可以达到在run方法内部使用synchronized占有对象同样的效果。
Public synchronized void heroisattcked(){
this.hp--;
}
Public synchronized void herocured(){
this.hp++;
}
线程的交互
如果对象血量过少,或者减血线程一直优先加血线程循环启动,那么对象的血量就会被减为负数,这显然是不利于人机交互的。所以,需要使用线程交互相关的方法来进行多线程交互。
相关方法:this.wait(),this.notify()
可以在线程安全的减血方法中加上if语句,当hp==1时,使用this.wait()让减血线程等待,并临时释放占有。这样加血线程就有机会占有对象,加血后再使用this.notify()的话就可以让在等待的减血线程苏醒过来。
Public synchronized void heroisattcked(){
if(hp==1) {
try {
this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
this.hp--;
}
Public synchronized void herocured(){
this.hp++;
this.notify();
}
不过以上代码仅适用于减血线程和加血线程数量相等的情况。
当减血线程多于加血线程,就需要修改启动this,wait()的条件为while(hp<=1)。加血线程多于减血线程时同理
//两个减血线程,一个加血线程(省略代码)
Thread t1=new Thread() {
public void run() {
while(true) {
//1.使用sleep方法睡眠100毫秒
try{
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
h.heroisattacked();
System.out.printf("英雄%s减血后剩下%d血%n", h.name,h.hp);
}
}
};
t1.setPriority(5);
t1.start();
Thread t3=new Thread() {
public void run() {
while(true) {
//1.使用sleep方法睡眠100毫秒
try{
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
h.heroisattacked();
System.out.printf("英雄%s减血后剩下%d血%n", h.name,h.hp);
}
}
};
Public synchronized void heroisattcked(){
while(hp<=1) {
try {
this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
this.hp--;
}
多线程的Lock方法
synchronized与Lock的区别
当一个线程运行了带有synchronized的方法,该线程会一直占有锁,待线程执行完毕才会释放锁,其他线程只能一直等待。试想:如果线程因为某些原因受到阻塞,无法释放锁的话,其他线程就只能一直等待,这会相当影响线程执行的效率。而Lock就能解决这个问题,Lock可以使用自带方法判断是否获取到锁(可以指定尝试获取锁的时间),亦能手动释放锁,这样就能解决线程一直等待的问题。
package socket;
import java.util.concurrent.locks.*;//导入Lock相关的打包类
import java.util.concurrent.TimeUnit;
public class TestThread {
static class Hero{
String name;
int hp;
public void heroisattacked() {
hp--;
}
public void herocured() {
hp++;
}
}
public static void main(String args[]) {
Hero h=new Hero();
h.name="凯";
h.hp=100;
//1.创建新锁,注意新锁lock相对于使用锁的同一批线程必须是同一个变量
Lock lock=new ReentrantLock();
Thread t1=new Thread() {
public void run() {
//2.将锁的开与关返回布尔值,避免重复关锁或者开锁而引起的程序错误
boolean locked=false;
while(true) {
try{
//3.truLock方法返回的为布尔值,获取到锁返回真,否则返回假
locked=lock.tryLock(1,TimeUnit.SECONDS);
if(locked) {
//与使用synchronized时的交互方法类似,避免血量减为负数
if(h.hp<=0) {
lock.unlock();
locked=false;
}
Thread.sleep(100);
h.heroisattacked();
System.out.printf("英雄%s减血后剩下%d血%n", h.name,h.hp);
}
}catch(InterruptedException e) {
e.printStackTrace();
}finally{//4.注意try里获取到的锁,要使用finally关闭
if(locked)
lock.unlock();
}
}
}
};
t1.setPriority(5);
t1.start();
Thread t3=new Thread() {
public void run() {
boolean locked=false;
while(true) {
//1.使用sleep方法睡眠100毫秒
try{
locked=lock.tryLock(1,TimeUnit.SECONDS);
if(locked) {
if(h.hp<=0) {
lock.unlock();
locked=false;
}
Thread.sleep(100);
h.heroisattacked();
System.out.printf("英雄%s减血后剩下%d血%n", h.name,h.hp);
}
}catch(InterruptedException e) {
e.printStackTrace();
}finally{
if(locked)
lock.unlock();
}
}
}
};
t3.setPriority(5);
t3.start();
Thread t2=new Thread() {
public void run() {
boolean locked=false;
while(true) {
//1.使用sleep方法睡眠100毫秒
try{
locked=lock.tryLock(1,TimeUnit.SECONDS);
if(h.hp>=100) {
lock.unlock();
locked=false;
}
Thread.sleep(100);
h.herocured();
System.out.printf("英雄%s加血后剩下%d血%n", h.name,h.hp);
}catch(InterruptedException e) {
e.printStackTrace();
}finally {
if(locked)
lock.unlock();
}
}
}
};
t2.setPriority(5);
t2.start();
}
}